package org.akomantoso.core.data;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.akomantoso.core.io.Externalizable;
import org.akomantoso.core.io.Input;
import org.akomantoso.core.io.Output;

/**
 * The complexType coreProperties lists the identifying properties available at
 * any of the FRBR hierarchy levels.
 * 
 * <pre>
 * <xsd:complexType name="coreProperties">
 *    <xsd:sequence>
 * 		<xsd:element ref="FRBRthis"/>
 * 		<xsd:element ref="FRBRuri" minOccurs="1" maxOccurs="unbounded"/>
 * 		<xsd:element ref="FRBRalias" minOccurs="0" maxOccurs="unbounded"/>
 * 		<xsd:element ref="FRBRdate"/>
 * 		<xsd:element ref="FRBRauthor" minOccurs="1" maxOccurs="unbounded"/>
 * 		<xsd:element ref="componentInfo" minOccurs="0" maxOccurs="1"/>
 * 		<xsd:element ref="preservation" minOccurs="0" maxOccurs="1"/>
 * 	  </xsd:sequence>
 * </xsd:complexType>
 * </pre>
 * 
 * @author <a href="mailto:jacques.militello@gmail.com">Jacques Militello</a>
 * 
 * @since http://www.akomantoso.org/1.0
 */
public abstract class CoreProperty implements Externalizable{

	// Mandatory
	private final FRBRthis _FRBRthis = new FRBRthis();
	
	// Mandatory (min 1)
	private final List<FRBRuri> _FRBRuris = new ArrayList<FRBRuri>();
	
	// Optional
	private List<FRBRalias> _FRBRaliases;
	
	// Mandatory
	private final FRBRdate _FRBRdate = new FRBRdate();
	
	// Mandatory (min 1)
	private List<FRBRauthor> _FRBRauthors = new ArrayList<FRBRauthor>();
	
	// Optional
	private ComponentInfo _componentInfo;
	
	// Optional
	private Preservation _preservation;
	
	public FRBRthis getFRBRthis() {
		return _FRBRthis;
	}

	public List<FRBRuri> getFRBRuris() {
		return _FRBRuris;
	}
	
	public void addFRBRuri(FRBRuri fRBRuri) {
		_FRBRuris.add(fRBRuri);
	}
	public List<FRBRalias> getFRBRaliases() {
		return _FRBRaliases;
	}
	public void setFRBRaliases(List<FRBRalias> fRBRaliases) {
		_FRBRaliases = fRBRaliases;
	}
	public FRBRdate getFRBRdate() {
		return _FRBRdate;
	}
	public List<FRBRauthor> getFRBRauthors() {
		return _FRBRauthors;
	}
	public void setFRBRauthors(List<FRBRauthor> fRBRauthors) {
		_FRBRauthors = fRBRauthors;
	}
	public void addFRBRauthor(FRBRauthor author) {
		if (_FRBRauthors == null) {
			_FRBRauthors = new ArrayList<FRBRauthor>();
		}
		_FRBRauthors.add(author);
	}
	public ComponentInfo getComponentInfo() {
		return _componentInfo;
	}
	public void setComponentInfo(ComponentInfo componentInfo) {
		_componentInfo = componentInfo;
	}
	public Preservation getPreservation() {
		return _preservation;
	}
	public void setPreservation(Preservation preservation) {
		_preservation = preservation;
	}
	
	/**
	 * @see org.akomantoso.core.io.Externalizable#writeExternal(org.akomantoso.core.io.Output)
	 */
	public <T extends Output> void writeExternal(T out) throws IOException {
		_FRBRthis.writeExternal(out);
		
		for (int i = 0, n = _FRBRuris.size(); i < n; i++) {
			_FRBRuris.get(i).writeExternal(out);
		}
	
		if (_FRBRaliases != null) {
			for (int i = 0, n = _FRBRaliases.size(); i < n; i++) {
				_FRBRaliases.get(i).writeExternal(out);
			}
		}
		
		_FRBRdate.writeExternal(out);
		
		for (int i = 0, n = _FRBRauthors.size(); i < n; i++) {
			_FRBRauthors.get(i).writeExternal(out);
		}
		
		if (_componentInfo != null) {
			_componentInfo.writeExternal(out);
		}
		
		if (_preservation != null) {
			_preservation.writeExternal(out);
		}
	}
	
	/**
	 * @see org.akomantoso.core.io.Externalizable#readExternal(org.akomantoso.core.io.Input)
	 */
	public <T extends Input> void readExternal(T in) throws IOException {
		
		in.readNextStartElement();
		
		if (FRBRthis.ELEMENT == in.getLocalName()) {
			_FRBRthis.readExternal(in);
			in.readNextStartElement();
		} else {
			throw new IOException("Element must be [" + FRBRthis.ELEMENT + "], it's [" + in.getLocalName() + "]");
		}
		
		if (FRBRuri.ELEMENT == in.getLocalName()) {
			FRBRuri uri;
			while (FRBRuri.ELEMENT.equals(in.getLocalName())) {
				_FRBRuris.add(uri = new FRBRuri());
				uri.readExternal(in);
				in.readNextStartElement();
			}
		} else {
			throw new IOException("Element must be [" + FRBRuri.ELEMENT + "], it's [" + in.getLocalName() + "]");
		}
		
		if (FRBRalias.ELEMENT == in.getLocalName()) {
			FRBRalias alias;
			_FRBRaliases = new ArrayList<FRBRalias>();
			while (FRBRalias.ELEMENT.equals(in.getLocalName())) {
				_FRBRaliases.add(alias = new FRBRalias());
				alias.readExternal(in);
				in.readNextStartElement();
			}
		}
		
		if (FRBRdate.ELEMENT == in.getLocalName()) {
			_FRBRdate.readExternal(in);
			in.readNextStartElement();
		} else {
			throw new IOException("Element must be [" + FRBRdate.ELEMENT + "], it's [" + in.getLocalName() + "]");
		}
		
		
		if (FRBRauthor.ELEMENT == in.getLocalName()) {
			FRBRauthor author;
			_FRBRauthors = new ArrayList<FRBRauthor>();
			while (FRBRauthor.ELEMENT.equals(in.getLocalName())) {
				_FRBRauthors.add(author = new FRBRauthor());
				author.readExternal(in);
				in.readNextStartElement();
			}
		} else {
			throw new IOException("Element must be [" + FRBRauthor.ELEMENT + "], it's [" + in.getLocalName() + "]");
		}
		
		if (ComponentInfo.ELEMENT == in.getLocalName()) {
			_componentInfo = new ComponentInfo();
			_componentInfo.readExternal(in);
		}
		
		if (Preservation.ELEMENT == in.getLocalName()) {
			_preservation = new Preservation();
			_preservation.readExternal(in);
			in.readNextStartElement();
		}
		
		if (in.isNotLastVersion()) {
			in.doBackwardCompatible(CoreProperty.class, this);
		}
		
	}
	
}